package org.jackysoft.service.impl;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jackysoft.data.AbstractDataProvider;
import org.jackysoft.entity.FieldAccess;
import org.jackysoft.entity.HtmlFormElement;
import org.jackysoft.entity.HtmlFormTemplate;
import org.jackysoft.entity.User;
import org.jackysoft.service.FieldAccessService;
import org.jackysoft.service.FormService;
import org.jackysoft.service.HtmlFormElementService;
import org.jackysoft.service.HtmlTemplateService;
import org.jackysoft.service.OsVariableService;
import org.jackysoft.util.WorkflowConstant;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;

@Service
public class FormServiceImpl extends AbstractDataProvider<HtmlFormElement>
		implements FormService {

	static final Log log = LogFactory.getLog(FormServiceImpl.class);
	@Resource
	private TemplateLoader templateLoader;
	@Resource
	private ObjectWrapper freemarkerObjectWrapper;
	@Resource
	private HtmlTemplateService htmlService;
	@Resource
	private OsVariableService varService;
	
	@Resource
	private HtmlFormElementService elementService;
	@Resource
	private FieldAccessService fieldService;

	@Override
	public String generateHtmlForm(int actionId, int stepId, long workflowId,
			Map<Integer, String> nextSteps, String templateId,
			
			boolean showOnly) {
		
		StringWriter sw = htmlService.loadToWriter(templateId);
	
		Collection<HtmlFormElement> htmlValues = elementService.findByWorkflow(workflowId);
		final Set<String> readonlyFields = Sets.newHashSet();
		Collection<FieldAccess> alist = fieldService.getOfReadonly(workflowId, stepId);
		if (alist != null && !alist.isEmpty()) {
			for(FieldAccess fa:alist)
			readonlyFields.add(fa.getReadOnly());
		}
		String base = System.getProperty("base");
		String sbase = System.getProperty("sbase");
		String mainweb = System.getProperty("myappbase");
		String cpath = System.getProperty("contextPath");

		StringWriter oout = new StringWriter();
		try {
			String html = generateHtml(sw.getBuffer().toString(), nextSteps, actionId,
					workflowId, sbase, base, showOnly, readonlyFields,
					htmlValues);
			Configuration cfg = new Configuration();
			cfg.setTemplateLoader(templateLoader);
			cfg.setObjectWrapper(freemarkerObjectWrapper);
			cfg.setDefaultEncoding("UTF-8");
			Template temp = cfg.getTemplate("showform.ftl");
			temp.setEncoding("UTF-8");
			Map<String,Object> root = Maps.newHashMap();
			if(templateId!=null)
				root.put("template",templateId);
			root.put("actionId", actionId);
			root.put("stepId", stepId);
			root.put("workflowId", workflowId);
			root.put("base", base);
			root.put("htmlCode", html);
			User user = null;
			//TODO 获取当前登陆用户
			root.put("localName", user.getLocalName());
			root.put("sbase", sbase);
			root.put("myappbase", mainweb);
			root.put("contextPath", cpath);
							
			StringWriter script = new StringWriter();
			temp.process(root, script);
			script.flush();
			oout.write(script.toString());			

		} catch (TemplateException e) {
			log.error(e.toString());
			e.printStackTrace();
		} catch (IOException e) {
			log.error(e.toString());
			e.printStackTrace();
		}

		return (oout.toString());
	}

	/**
	 * @param readonlyFields
	 * 
	 * 
	 * */
	private String generateHtml(String html, Map<Integer, String> nextSteps,
			int actionId, long workflowId, String sbase, String base,
			boolean showOnly, Set<String> readonlyFields,
			Collection<HtmlFormElement> htmlValues) throws IOException {

		/***************************************************
		 * 
		 * 以下形式改用jsoup来修改html元素
		 * 
		 ***************************************************/
		Document doc = Jsoup.parse(html);
		Element head = doc.select("head").first();
		Element body = doc.select("body").first();
		processHtml(doc, readonlyFields, htmlValues);
		String[] scriptsrc = { 
				"scripts/jquery-1.8.3.js",				
				 "scripts/blockUI/jquery.blockUI.js",
				"scripts/tools.js"
				 };
		Element baseElement = doc.createElement("base");
		baseElement.attr("href", sbase);
		head.appendChild(baseElement);
		
		for (String jsSrc : scriptsrc) {
			head.appendElement("script").attr("language", "javascript")
					.attr("type", "text/javascript").attr("src", jsSrc)
					.text("\t\r\n");
		}
		head.appendElement("meta").attr("http-equiv", "contentType")
				.attr("content", "text/html; charset=utf-8");

		Element ctrldiv = doc.createElement("div").addClass("ctrldiv");
		Element firstChild = body.child(0);
		firstChild.before(ctrldiv);
		if (!showOnly) {
			if (nextSteps != null) {
				for (Integer key : nextSteps.keySet()) {
					ctrldiv.appendElement("button").attr("type", "button")
							.attr("step", key + "")
							.attr("action", actionId + "")
							.attr("entityId", workflowId + "")
							.addClass("gonext").text(nextSteps.get(key));
				}

			}
			ctrldiv.appendElement("button").addClass("closeForm").text("关闭不保存");
		}
		ctrldiv.appendElement("button").addClass("otherAttachment").text("附件");
		ctrldiv.appendElement("div").attr("id", "attachments").text("\r\n\t");
		String htmldoc = doc.html();
		return htmldoc;
	}

	/**
	 * 把doc 里 readonlys的元素设置成 readonly
	 * 
	 * @param doc
	 *            dom4j文档
	 * @param readonlys
	 *            需要设置成只读的元素名称
	 * @param values
	 *            所有页面元素
	 * */
	protected void processHtml(Document doc, Set<String> readonlys,
			Collection<HtmlFormElement> values) {
		Element body = doc.select("body").first();
		Elements inputs = body.select("form input");
		setAttrs(inputs, readonlys, true, values);
		// 取select 元素
		inputs = body.select("form select");
		setAttrs(inputs, readonlys, false, values);
		// 取textarea元素
		inputs = body.select("form textarea");
		setAttrs(inputs, readonlys, true, values);
		// 取button元素
		inputs = body.select("form  button");
		setAttrs(inputs, readonlys, false, Collections.<HtmlFormElement> emptyList());

       

		

	}

	/**
	 * 
	 * */
	void setAttrs(Elements inputs, Set<String> readonlys, boolean trimValue,
			Collection<HtmlFormElement> values) {
		for (Iterator<Element> itr = inputs.iterator(); itr.hasNext();) {
			Element input = itr.next();
			String name = StringUtils.trimToEmpty(input.attr("name"));

			for (HtmlFormElement se : values) {
				String tagName = input.tagName();
				String sName = StringUtils.trimToEmpty(se.getName());
				if ("input".equalsIgnoreCase(tagName)) {
					if (sName.equalsIgnoreCase(name)) {
						String type = input.attr("type");
						if (StringUtils.trimToNull(type) == null)
							type = "text";

						if ("radio".equalsIgnoreCase(type)
								|| "checkbox".equalsIgnoreCase(type)) {
							if (StringUtils.isNotEmpty(se.getChecked()))
								input.attr("checked", "checked");
						}
					}
					

				}
				if("select".equalsIgnoreCase(tagName)){
					if (sName.equalsIgnoreCase(name)) {
					   Elements opts = input.children();
					   input.attr("svalue",StringUtils.trimToNull(se.getValue()));
					   for(Iterator<Element> eitr = opts.iterator();eitr.hasNext();){
						   String sval = StringUtils.trimToNull(se.getValue());
						   Element opt = eitr.next();
						   if(sval!=null){
							   if(sval.equalsIgnoreCase(opt.val())){
								   opt.attr("selected", "selected");
								   
								   break;
							   }
						   }
					   }
					}
				}
				
				if (sName.equalsIgnoreCase(name)) {
					input.val(StringUtils.trimToEmpty(se.getValue()));
				}

			}

			if (trimValue)
				input.val(StringUtils.trimToEmpty(input.val()));
			for (String rname : readonlys) {
				if (rname.equalsIgnoreCase(name))
					input.attr("disabled", "disabled");
			}

		}
	}

	void setAttrsDisabled(Elements inputs){
	    for (Iterator<Element> itr = inputs.iterator(); itr.hasNext();) {
			Element input = itr.next();
		    input.attr("disabled", "true");
		}
	}

	@Override
	public String extractHtmlElementsToJson(HtmlFormTemplate temp) throws Exception {
		if(temp==null) return "[]";
		InputStream input = new BufferedInputStream(new FileInputStream(new File(
				String.format("%s%s%s", context.HTMLBase,File.separator,temp.getPath())
				)));
		StringWriter writer = new StringWriter();
		IOUtils.copy(input, writer);
		Collection<HtmlFormElement> elements = extractHtmlElements(writer.getBuffer().toString());
		GsonBuilder builder = new GsonBuilder();
		builder.excludeFieldsWithoutExposeAnnotation();
		Gson gson = builder.create();
		StringWriter jsonwriter = new StringWriter();
		gson.toJson(elements, jsonwriter);
		return jsonwriter.toString();
	}

	private Collection<HtmlFormElement> extractHtmlElements(String html) {
		Collection<HtmlFormElement> elements = new ArrayList<HtmlFormElement>();
		Document doc = Jsoup.parse(html);
		Element body = doc.select("body").first();
		Elements inputs = body.select("form input");
		// 先取input 元素 type=text
		for (Iterator<Element> itr = inputs.iterator(); itr.hasNext();) {
			Element input = itr.next();
			String type = input.attr("type");
			if (type == null)
				type = "text";
			if (INPUT_TYPES.contains(type.toLowerCase())) {
				HtmlFormElement el = new HtmlFormElement();
				el.setId(input.attr("id"));
				el.setName(input.attr("name"));
				el.setTitle(input.attr("title"));
				el.setChecked(input.attr("checked"));
				el.setEnabled(input.attr("enabled"));
				el.setDisabled(input.attr("disabled"));
				elements.add(el);
			}
		}
		// 取select 元素
		inputs = body.select("form select");
		for (Iterator<Element> itr = inputs.iterator(); itr.hasNext();) {
			Element select = itr.next();
			HtmlFormElement el = new HtmlFormElement();
			el.setId(select.attr("id"));
			el.setName(select.attr("name"));
			el.setTitle(select.attr("title"));
			el.setChecked(select.attr("checked"));
			el.setEnabled(select.attr("enabled"));
			el.setDisabled(select.attr("disabled"));
			elements.add(el);
		}
		// 取textarea元素
		inputs = body.select("form textarea");
		for (Iterator<Element> itr = inputs.iterator(); itr.hasNext();) {
			Element select = itr.next();
			HtmlFormElement el = new HtmlFormElement();
			el.setId(select.attr("id"));
			el.setName(select.attr("name"));
			el.setTitle(select.attr("title"));
			el.setChecked(select.attr("checked"));
			el.setEnabled(select.attr("enabled"));
			el.setDisabled(select.attr("disabled"));

			elements.add(el);
		}
		// 取button元素
		inputs = body.select("form  button");
		for (Iterator<Element> itr = inputs.iterator(); itr.hasNext();) {
			Element select = itr.next();
			HtmlFormElement el = new HtmlFormElement();
			el.setId(select.attr("id"));
			el.setName(select.attr("name"));
			el.setTitle(select.attr("title"));
			el.setChecked(select.attr("checked"));
			el.setEnabled(select.attr("enabled"));
			el.setDisabled(select.attr("disabled"));

			elements.add(el);
		}
		return elements;
	}

	@Override
	public String generateHtmlForm(long refid, String templateId
			) {
		/***************************************************
		 * 
		 * 以下形式改用jsoup来修改html元素
		 * 
		 ***************************************************/
		String base = System.getProperty("base");
		String sbase = System.getProperty("sbase");
		Collection<HtmlFormElement> htmlValues = elementService.findByWorkflow(refid);
		
		StringWriter sw = htmlService.loadToWriter(templateId);
		Document doc = Jsoup.parse(new String(sw.toString()));
		Element head = doc.select("head").first();
		Set<String> readonlyFields = Sets.newHashSet();
		for (HtmlFormElement sh : htmlValues) {
			readonlyFields.add(sh.getName());
		}
		processHtml(doc, readonlyFields, htmlValues);
		Element body = doc.select("body").first();
		Elements inputs = body.select("form input[type=button]");
		setAttrsDisabled(inputs);
		String[] scriptsrc = {  "scripts/jquery-1.8.3.js", "scripts/tools.js"};
		Element baseElement = doc.createElement("base");
		baseElement.attr("href", sbase);
		head.appendChild(baseElement);
		
		for (String jsSrc : scriptsrc) {
			head.appendElement("script").attr("language", "javascript")
					.attr("type", "text/javascript").attr("src", jsSrc)
					.text("\t\r\n");
		}
		head.appendElement("meta").attr("http-equiv", "contentType")
				.attr("content", "text/html; charset=utf-8");
		StringWriter out = new StringWriter();

		try {
			Configuration cfg = new Configuration();
			cfg.setTemplateLoader(templateLoader);
			cfg.setObjectWrapper(freemarkerObjectWrapper);
			cfg.setDefaultEncoding("UTF-8");
			Template temp = cfg.getTemplate("showform.ftl");
			temp.setEncoding("UTF-8");
			Map<String, Object> root = new HashMap<String, Object>();

			String titleVar = varService.getValue(refid, WorkflowConstant.FORM_TITLE.toString());
			root.put("formtitle", titleVar);
			root.put("workflowId", refid);
			root.put("actionId", 0);
			root.put("stepId", 0);
			root.put("base", base);
			root.put("htmlCode", doc.html());
			User user = null;
			//TODO 获取当前用户
			root.put("localName", user.getLocalName());
			root.put("sbase", sbase);
			temp.process(root, out);

		} catch (TemplateException e) {
			log.error(e.toString());
			e.printStackTrace();
		} catch (IOException e) {
			log.error(e.toString());
			e.printStackTrace();
		}

		return out.toString();
	}

}
